package client.view;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Point;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetAdapter;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.event.ActionEvent;
import java.io.IOException;

import javax.swing.AbstractAction;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.border.LineBorder;

import commands.MoveCardCommand;

import core.Card;
import core.Location;
import core.OrderedZone;
import core.Visibility;
import core.Zone;

public class ZoneView extends AbstractView {
	/**
	 * Creates a new ZoneView visualization for the given Zone and initializes its
	 * interface.
	 * @param z
	 */
	public ZoneView(Zone z, client.Client c) {
		super(z, new JPanel(), c);
		zone = z;

		JMenu visibilityMenu = new JMenu("Visibility");
		visibilityMenu.add(new SetVisibilityAction("All", Visibility.ALL, z, client));
		visibilityMenu.add(new SetVisibilityAction("None", Visibility.NONE, z, client));
		visibilityMenu.add(new SetVisibilityAction("Deafult", zone.getDefaultVisibility(),z,client));
		popupMenu.add(visibilityMenu);
		JMenu sizeMenu = new JMenu("Size");
		sizeMenu.add(new javax.swing.AbstractAction("Double") {
			public void actionPerformed(ActionEvent arg0) {
				Dimension d = new Dimension(component.getWidth() * 2, component.getHeight() * 2);
				component.setSize(d);
				component.setMaximumSize(d);
			}});
		sizeMenu.add(new javax.swing.AbstractAction("Halve") {
			public void actionPerformed(ActionEvent arg0) {
				Dimension d = new Dimension((int)(component.getWidth() * 0.5), (int)(component.getHeight() * 0.5));
				component.setSize(d);
				component.setMaximumSize(d);
			}});
		popupMenu.add(sizeMenu);
		popupMenu.add(new javax.swing.JMenuItem(new AbstractAction("Shuffle") {
			public void actionPerformed(java.awt.event.ActionEvent e) {
				try {
					client.sendCommand(new commands.ShuffleZoneCommand(zone));
				} catch (java.io.IOException e1) {e1.printStackTrace();}
			}
		}));
		createGUI();
	}

	/**
	 * Initializes the interface for a ZoneView.
	 */
	private void createGUI()
	{
		// This is really ugly, but it's the price we pay for thread-safety: we
		// create the CardView here so that if the new card gets modified
		// before the event dispatch thread gets around to our request,
		// there'll still be somebody to receive the update.
		zone.addZoneObserver(new core.ZoneObserver() {
			public void cardAdded(Card card) {
				final CardView cardView = new CardView(card, client);
				SwingUtilities.invokeLater(new Runnable() {
					public void run() {
						addCardView(cardView);
					}
				});
			}

			// this is even uglier, but we need something final
			public void cardRemoved(Card card) {
				CardView temp = null;
				for (CardView cardView : cardViews)
					if (cardView.getModel() == card) {
						temp = cardView;
						break;
					}
				final CardView cardView = temp;
				SwingUtilities.invokeLater(new Runnable() {
					public void run() {
						removeCardView(cardView);
					}
				});
			}

		});

		component.setBackground(COLOR);
		component.setBorder(new LineBorder(Color.BLACK, 1));
		component.setLayout(null);
		Dragger dragger = new Dragger(true);
		component.addMouseMotionListener(dragger);
		component.addMouseListener(dragger);
		component.setSize(new Dimension(300, 200));
		component.setMaximumSize(new Dimension(300, 200));
		String zoneTitle = zone.getName();

		JLabel label = new JLabel(zoneTitle);
		label.setOpaque(false);
		label.setForeground(Color.WHITE);
		label.setFont(new Font("Helvetica",Font.PLAIN,10));
		component.add(label);
		label.setSize(200,10);
		label.setLocation(1, component.getHeight()-20);

		new DropTarget( component, new DropTargetAdapter() {
			public void drop(DropTargetDropEvent dtde) {
				if (dtde.getTransferable().isDataFlavorSupported(CardMoveTransferable.CARD_MOVE_FLAVOR))
					try
					{
						dtde.acceptDrop( DnDConstants.ACTION_MOVE );
						CardMoveTransferable.CardMove move =
						  (CardMoveTransferable.CardMove)dtde.getTransferable().getTransferData(CardMoveTransferable.CARD_MOVE_FLAVOR);

						Point loc = null;
						for (Card c : move.getCards()) {
							MoveCardCommand mcc = new MoveCardCommand(c,  zone);
							// If moving into a spatial zone...
							if (!(zone instanceof OrderedZone)) {
								if (loc == null)
									loc = dtde.getLocation();
								else
									loc.x += 10;
								mcc.setLocation(new Location(loc));
								if (c.getLocation() != null)
									mcc.getLocation().setRotation(c.getLocation().getRotation());
							}
	
							client.sendCommand(mcc);
						}
						SelectionRegistry.clear();
						dtde.getDropTargetContext().dropComplete(true);
						return;
					} catch (IOException e) {
						e.printStackTrace();
					} catch (UnsupportedFlavorException e) {
						e.printStackTrace();
					}
				dtde.rejectDrop();
			}
		});

		for (Card c: zone.getCards())
			addCardView(new CardView(c, client));
		updateView();
	}
	
	/**
	 * Adds a CardView to the Zone visualization.
	 */
	protected void addCardView(CardView cardView) {
		cardViews.add(cardView);
		component.add(cardView.getComponent());
		component.setComponentZOrder(cardView.getComponent(),0);
		updateView();
	}

	/**
	 * Removes a CardView from the Zone visualization.
	 */
	protected void removeCardView(CardView cardView) {
		cardViews.remove(cardView);
		component.remove(cardView.getComponent());
		updateView();
		component.repaint();
	}

	/**
	 * Returns an unmodifiable list of the CardViews present in the ZoneView.
	 */
	public java.util.List<CardView> getCardViews() {
		return java.util.Collections.unmodifiableList(cardViews);
	}
	
	public void updateView() {
		for (CardView cardView : cardViews)
			cardView.getComponent().setLocation(((Card)cardView.getModel()).getLocation().getPosition());
	}

	protected final Zone zone;
	protected final java.util.List<CardView> cardViews = new java.util.LinkedList<CardView>();

	private static final Color COLOR = new Color(52,108,56).darker();
}